﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;

namespace ElectronicCommerce.Framework.Utility.Pagination
{

    [CollectionDataContract]
    public class PagedList<T> : List<T>, IPagedList
    {
        #region ctor

        public PagedList() { }

        public PagedList(IEnumerable<T> source, int index, int pageSize)
        {
            if (source is IQueryable<T>)
                Initialize(source as IQueryable<T>, index, pageSize);
            else
                Initialize(source.AsQueryable(), index, pageSize);
        }

        public PagedList(IQueryable<T> source, int index, int pageSize)
        {
            Initialize(source, index, pageSize);
        }

        public PagedList(List<T> source, int index, int pageSize, int totalCount)
        {
            Initialize(source, index, pageSize, totalCount);
        }

        public PagedList(IEnumerable<T> source, int index, int pageSize, int totalCount)
        {
            Initialize(source, index, pageSize, totalCount);
        }

        #endregion

        #region IPagedList Members

        [DataMember]
        public int PageCount { get; private set; }

        [DataMember]
        public int TotalItemCount { get; private set; }

        [DataMember]
        public int PageIndex { get; private set; }

        public int PageNumber { get { return PageIndex + 1; } }

        [DataMember]
        public int PageSize { get; private set; }

        [DataMember]
        public bool HasPreviousPage { get; private set; }

        [DataMember]
        public bool HasNextPage { get; private set; }

        [DataMember]
        public bool IsFirstPage { get; private set; }

        [DataMember]
        public bool IsLastPage { get; private set; }

        #endregion

        protected void Initialize(List<T> source, int index, int pageSize, int totalCount)
        {
            //### set properties
            TotalItemCount = totalCount;
            PageSize = pageSize;
            PageIndex = index;
            if (TotalItemCount > 0)
                PageCount = (int)Math.Ceiling(TotalItemCount / (double)PageSize);
            else
                PageCount = 0;
            HasPreviousPage = (PageIndex > 0);
            HasNextPage = (PageIndex < (PageCount - 1));
            IsFirstPage = (PageIndex <= 0);
            IsLastPage = (PageIndex >= (PageCount - 1));

            //### argument checking
            if (index < 0)
                throw new ArgumentOutOfRangeException("PageIndex cannot be below 0.");
            if (pageSize < 1)
                throw new ArgumentOutOfRangeException("PageSize cannot be less than 1.");

            //### add items to internal list

            AddRange(source);
        }

        protected void Initialize(IEnumerable<T> onlyOnePageData, int index, int pageSize, int totalCount)
        {
            //### set properties
            TotalItemCount = totalCount;
            PageSize = pageSize;
            PageIndex = index;
            if (TotalItemCount > 0)
            {
                PageCount = (int)Math.Ceiling(TotalItemCount / (double)PageSize);
            }
            else
            {
                PageCount = 0;
            }
            HasPreviousPage = (PageIndex > 0);
            HasNextPage = (PageIndex < (PageCount - 1));
            IsFirstPage = (PageIndex <= 0);
            IsLastPage = (PageIndex >= (PageCount - 1));

            //### argument checking
            if (index < 0)
            {
                throw new ArgumentOutOfRangeException("PageIndex cannot be below 0.");
            }
            if (pageSize < 1)
            {
                throw new ArgumentOutOfRangeException("PageSize cannot be less than 1.");
            }

            //### add items to internal list
            this.Clear();
            this.AddRange(onlyOnePageData);

        }

        protected void Initialize(IQueryable<T> source, int index, int pageSize)
        {
            //### set source to blank list if source is null to prevent exceptions
            if (source == null)
                source = new List<T>().AsQueryable();

            var sourceList = source.ToList();
            //### set properties
            TotalItemCount = sourceList.Count();
            PageSize = pageSize;
            PageIndex = index;
            if (TotalItemCount > 0)
                PageCount = (int)Math.Ceiling(TotalItemCount / (double)PageSize);
            else
                PageCount = 0;
            HasPreviousPage = (PageIndex > 0);
            HasNextPage = (PageIndex < (PageCount - 1));
            IsFirstPage = (PageIndex <= 0);
            IsLastPage = (PageIndex >= (PageCount - 1));

            //### argument checking
            if (index < 0)
                throw new ArgumentOutOfRangeException("PageIndex cannot be below 0.");
            if (pageSize < 1)
                throw new ArgumentOutOfRangeException("PageSize cannot be less than 1.");

            //### add items to internal list
            if (TotalItemCount > 0 && TotalItemCount <= pageSize)
            {
                AddRange(sourceList);
            }
            else if (TotalItemCount > 0)
            {
                AddRange(sourceList.Skip((index) * pageSize).Take(pageSize).ToList());
            }
        }
    }

    public static class Pagination
    {
        public static PagedList<T> ToPagedList<T>(this List<T> source, int pageNumber, int pageSize, int totalCount)
        {
            if (pageNumber <= 0 || pageSize <= 0)
            {
                if (totalCount <= 0) totalCount = 10;
                return new PagedList<T>(source, 0, totalCount);
            }
            else
            {
                return new PagedList<T>(source, pageNumber - 1, pageSize, totalCount);
            }
        }

        public static PagedList<T> ToPagedList<T>(this IQueryable<T> source, int pageNumber, int pageSize)
        {
            if (pageNumber <= 0 || pageSize <= 0)
            {
                var sourceList = source.ToList();
                int totalCount = sourceList.Count();

                if (totalCount <= 0) totalCount = 10;
                return new PagedList<T>(sourceList, 0, totalCount);
            }
            else
            {
                return new PagedList<T>(source, pageNumber - 1, pageSize);
            }
        }

        public static PagedList<T> ToPagedList<T>(this IEnumerable<T> source, int pageNumber, int pageSize)
        {
            if (pageNumber <= 0 || pageSize <= 0)
            {
                int totalCount = source.Count();
                if (totalCount <= 0) totalCount = 10;
                return new PagedList<T>(source, 0, totalCount);
            }
            else
            {
                return new PagedList<T>(source, pageNumber - 1, pageSize);
            }
        }

        public static PagedList<T> ToPagedList<T>(this IEnumerable<T> source, int pageNumber, int pageSize
            , int totalCount, bool uglyParamter)
        {
            if (pageNumber <= 0 || pageSize <= 0)
            {
                if (totalCount <= 0)
                {
                    totalCount = 10;
                }
                return new PagedList<T>(source, 0, pageSize, totalCount);
            }
            else
            {
                return new PagedList<T>(source, pageNumber - 1, pageSize, totalCount);
            }
        }
    }
}
